
#include <WiFi.h>
#include <string.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <httpUpdate.h>
#include <ArduinoJson.h>
#include <HardwareSerial.h>

#include <WiFiUdp.h>
#include <TFT_eSPI.h>
#include <BLEDevice.h>
#include <driver/ledc.h>
#include "soc/rtc_wdt.h" //设置rtc看门狗用

// Mppt相关变量
//  充电相关变量
float chargeVoltage = 0;
float chargeCurrent = 0;
// 光伏板相关变量
float pvVoltage = 0;
float pvCurrent = 0;
float pvPower = 0;
// 控制器温度变量
float controllerTemperature = 0;
// 累计功率变量
float cumulativePower = 0;

// 电池组相关变量
// 电压，单位伏特（V）
float batteryVoltage = 0;
// 电流，单位安培（A）
float batteryCurrent = 0;
// 功率，单位瓦特（W）
float batteryPower = 0;
// 电量，单位百分号（%）
float batteryCharge = 0;
// 温度，单位摄氏度（°C）
float batteryTemperature = 0;

// 并网传感器相关变量
//  定义用电侧的电压变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usageVoltage = 0;
// 定义用电侧的电流变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usageCurrent = 0;
// 定义用电侧的功率变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usagePower = 0;
// 定义用电侧的电能变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usageEnergy = 0;
// 定义用电侧的功率因素变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usagePowerFactor = 0;
// 定义用电侧的频率变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float usageFrequency = 0;

// 定义并网侧的电压变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridVoltage = 0;
// 定义并网侧的电流变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridCurrent = 0;
// 定义并网侧的功率变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridPower = 0;
// 定义并网侧的电能变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridEnergy = 0;
// 定义并网侧的功率因素变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridPowerFactor = 0;
// 定义并网侧的频率变量的实际存储对象，初始值未指定，后续会从传感器读取数据赋值
float gridFrequency = 0;

float gridEfficiency = 0;

// 保存重启原因
String rebootReason = "";

// 定义按键引脚
const int LedBlink = 2;
const int buttonPin1 = 32;
const int buttonPin2 = 33;

void saveConfig();
void sendtoData(String p);

/**
 * 结构体，用于存储配网信息
 */
struct config_type
{
  int currentScreen = 0;
  int switchArray[8] = {0};
  bool grdOnOff = true;
  uint8_t magic;
};
config_type config;
WiFiClient TCPclient;

#include "esp32_udp.h"
#include "esp32_acs.h"
#include "esp32_azk.h"
#include "esp32_grd.h"
#include "esp32_tft.h"
#include "esp32_bfy.h"

volatile bool button1 = false;
volatile bool button2 = false;
unsigned long debounceDelay = 10;   // 防抖延时时间，单位毫秒
unsigned long lastDebounceTime = 0; // 上一次防抖处理的时间

/**
 * 存储信息
 */
void saveConfig()
{
  EEPROM.begin(64);
  uint8_t *p = (uint8_t *)(&config);
  for (int i = 0; i < int(sizeof(config)); i++)
  {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}
/**
 * 加载存储的信息，并检查是否恢复出厂信息
 */
void loadConfig()
{
  EEPROM.begin(64);
  uint8_t *p = (uint8_t *)(&config);
  for (int i = 0; i < int(sizeof(config)); i++)
  {
    *(p + i) = EEPROM.read(i);
  }
  if (config.magic != 0xAA)
  {
    Serial.println("重写数据");
    config.magic = 0xAA;
    config.currentScreen = 0;
    config.grdOnOff = true;
    for (size_t i = 0; i < 8; i++)
    {
      config.switchArray[i] = 0;
    }
    saveConfig();
  }
  else
  {
    Serial.println("读取成功......");
  }
}

void current(bool c)
{
  static bool a = false;
  if (c)
  {
    currentScreen++;
    if (currentScreen >= 3)
    {
      currentScreen = 0;
    }
  }
  else
  {
    currentScreen--;
    if (currentScreen <= -1)
    {
      currentScreen = 2;
    }
  }
}

// 按键1中断处理函数
void IRAM_ATTR
button1_ISR()
{
  unsigned long currentTime = millis();
  static unsigned long prssTime = 0;
  if (currentTime - lastDebounceTime > debounceDelay)
  {
    if (prssTime == 0)
    {
      prssTime = currentTime;
    }
    else if (currentTime - prssTime >= debounceDelay)
    {
      // 在这里添加按键释放的处理逻辑
      current(0);
      prssTime = 0;
    }
    else
    {
      prssTime = 0;
    }
    lastDebounceTime = currentTime;
  }
}

// 按键2中断处理函数
void IRAM_ATTR button2_ISR()
{
  unsigned long currentTime = millis();
  static unsigned long prssTime = 0;
  if (currentTime - lastDebounceTime > debounceDelay)
  {
    if (prssTime == 0)
    {
      prssTime = currentTime;
    }
    else if (currentTime - prssTime >= debounceDelay)
    {
      // 在这里添加按键释放的处理逻辑
      current(1);
      prssTime = 0;
    }
    else
    {
      prssTime = 0;
    }
    lastDebounceTime = currentTime;
  }
}

void Blink()
{
  static long lastBlinkTime = 0;
  // 检查 4 个引脚状态
  if (!digitalRead(serial1TxPin) || !digitalRead(serial2TxPin))
  {
    if (!digitalRead(LedBlink))
    {                               // LED 未点亮时
      digitalWrite(LedBlink, HIGH); // 点亮 LED
      lastBlinkTime = millis();     // 记录点亮时间
    }
  }
  else
  {
    if (digitalRead(LedBlink))
    { // LED 点亮时
      unsigned long currentTime = millis();
      if (currentTime - lastBlinkTime > 50)
      {
        digitalWrite(LedBlink, LOW); // 熄灭 LED
      }
    }
  }
}

const char *getResetReasonText(esp_reset_reason_t reason)
{
  switch (reason)
  {
  case ESP_RST_UNKNOWN:
    return "未知";
  case ESP_RST_POWERON:
    return "上电";
  case ESP_RST_EXT:
    return "外部";
  case ESP_RST_SW:
    return "软件";
  case ESP_RST_PANIC:
    return "异常";
  case ESP_RST_INT_WDT:
    return "中断";
  case ESP_RST_TASK_WDT:
    return "任务";
  case ESP_RST_WDT:
    return "看门";
  case ESP_RST_DEEPSLEEP:
    return "深度";
  case ESP_RST_BROWNOUT:
    return "欠压";
  case ESP_RST_SDIO:
    return "SDIO";
  default:
    return "无效";
  }
}

// 主设置函数
void setup()
{
  Serial.begin(115200);
  Serial.println("启动......");
  esp_reset_reason_t reason = esp_reset_reason();
  rebootReason = getResetReasonText(reason);
  rtc_wdt_protect_off();                   // 看门狗写保护关闭 关闭后可以喂狗
  rtc_wdt_enable();                        // 启用看门狗
  rtc_wdt_set_time(RTC_WDT_STAGE0, 30000); // 设置看门狗超时 8000ms.则reset重启
  pinMode(LedBlink, OUTPUT);
  pinMode(buttonPin1, INPUT_PULLUP);                                       // 设置按键1引脚为输入上拉模式
  pinMode(buttonPin2, INPUT_PULLUP);                                       // 设置按键2引脚为输入上拉模式
  attachInterrupt(digitalPinToInterrupt(buttonPin1), button1_ISR, CHANGE); // 配置按键1的中断
  attachInterrupt(digitalPinToInterrupt(buttonPin2), button2_ISR, CHANGE); // 配置按键2的中断
  loadConfig();
  setupTft();
  setupAcs();
  setupAizk();
}

// 主循环函数
void loop()
{
  Blink();
  loopTft();
  loopBfy();
  loopUdp();
  loopAcs();
  loopAizk();
  loopGrid();
  rtc_wdt_feed(); // 喂狗函数
}
